package com.xyy.saas.payment.adpater.config;

import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigChangeListener;
import com.ctrip.framework.apollo.ConfigService;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.xyy.saas.payment.adpater.chinabank.constants.ChinaBankBusinessConfig;
import com.xyy.saas.payment.adpater.fumin.config.FuminBankBusinessConfig;
import com.xyy.saas.payment.adpater.fumin.config.FuminBankPayConfig;
import com.xyy.saas.payment.adpater.fumin.config.ProfitSharingAccountConfig;
import com.xyy.saas.payment.common.Constants;
import com.xyy.saas.payment.common.constants.ApolloConstants;
import com.xyy.saas.payment.common.enums.AmountTypeEnum;
import com.xyy.saas.payment.common.exception.PaymentException;
import com.xyy.saas.payment.cores.enums.BusinessTypeEnum;
import com.xyy.saas.payment.dao.model.ThirdUserInfo;
import com.xyy.saas.payment.service.ThirdUserInfoService;
import com.xyy.saas.payment.util.ApolloUtil;
import com.xyy.saas.payment.util.JSONUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.*;

/**
 * @author wangtianqi
 * @date 2020/7/28 13:20
 */
@Slf4j
@Component
public class BankConfigManager implements InitializingBean {
    @Resource
    private ThirdUserInfoService thirdUserInfoService;

    private Map<String, ChinaBankBusinessConfig> chinaBankMap = new HashMap<>();
    private Map<String, FuminBankBusinessConfig> fuminBankMap = new HashMap<>();
    private Map<String, FuminBankPayConfig> fuminBankPayConfigMap = new HashMap<>();
    private Map<String, List<ProfitSharingAccountConfig>> profitSharingAccountConfig = new HashMap<>();

    /**
     * 获取结算账户
     */
    public String getProfitSharingAccount(String businessOrderType, AmountTypeEnum amountType) throws PaymentException {
        if(profitSharingAccountConfig.isEmpty()) {
            throw new PaymentException("Profit sharing account not configured with Apollo");
        }

        List<ProfitSharingAccountConfig> items = profitSharingAccountConfig.get(businessOrderType);

        if(CollectionUtils.isEmpty(items)) {
            throw new PaymentException("Profit sharing account not configured with " + businessOrderType);
        }

        for(ProfitSharingAccountConfig item : items) {
            String account = item.matchAccount(amountType);

            if(!StringUtils.isEmpty(account)) {
                return account;
            }
        }

        throw new PaymentException("Profit sharing account not fount with businessOrderType=" +
                businessOrderType + ", amountType=" + amountType.name());
    }

    /**
     * 初始化配置
     */
    private void initConfig() {
        Map<String, String> configs = JSONUtils.parse2Map(ApolloUtil.getProperty(ApolloConstants.BANK_BUSINESS_ORDER_CONFIG_NEW), String.class, String.class);

        if(configs == null) {
            return;
        }

        Map<String, ChinaBankBusinessConfig> tmpChinaBankMap = new HashMap<>(16);
        Map<String, FuminBankBusinessConfig> tmpFuminBankMap = new HashMap<>(16);

        for(String bankType : configs.keySet()) {
            switch (bankType) {
                case Constants.CHANNEL_CHINA_BANK_NAME:
                    castVal2ChinaBankConfig(configs.get(bankType), tmpChinaBankMap);
                    continue;
                case Constants.CHANNEL_FUMIN_BANK_NAME:
                    castVal2FuminBankConfig(configs.get(bankType), tmpFuminBankMap);
                    continue;
                default:
            }
        }

        if(!tmpChinaBankMap.isEmpty()) {
            chinaBankMap = tmpChinaBankMap;
        }

        if(!tmpFuminBankMap.isEmpty()) {
            fuminBankMap = tmpFuminBankMap;
        }
    }

    private void initFuminPayConfig() {
        Map<String, FuminBankPayConfig> tmpfuminBankPayConfigMap = new HashMap<>(16);
        String val = ApolloUtil.getProperty(ApolloConstants.BANK_FUMIN_PAY_CONFIG);
        List<FuminBankPayConfig> list = JSONUtils.parse2List(val, FuminBankPayConfig.class);

        for(FuminBankPayConfig c : list) {
            tmpfuminBankPayConfigMap.put(c.getBusinessOrderType(), c);
        }

        fuminBankPayConfigMap = tmpfuminBankPayConfigMap;
    }

    private void initProfitSharingAccountConfig() {
        Map<String, String> configs = JSONUtils.parse2Map(ApolloUtil.getProperty(ApolloConstants.PROFIT_SHARING_ACCOUNT_CONFIG), String.class, String.class);

        if(configs == null || configs.isEmpty()) {
            return;
        }

        Map<String, List<ProfitSharingAccountConfig>> map = new HashMap<>(16);

        configs.entrySet().forEach(config -> {
            map.put(config.getKey(), JSONUtils.parse2List(config.getValue(), ProfitSharingAccountConfig.class));
        });

        if(!map.isEmpty()) {
            profitSharingAccountConfig = map;
        }
    }

    public String getPrivateKeyByMch(String mch) {
        return ApolloUtil.getProperty(Constants.CHANNEL_CHINA_BANK_NAME + "_privatekey_" + mch);
    }

    public String getPublicKeyByMch(String mch) {
        return ApolloUtil.getProperty(Constants.CHANNEL_CHINA_BANK_NAME + "_publickey_" + mch);
    }

    public String getChinaBankAccessNo(String businessOrderType) {
       return chinaBankMap.get(businessOrderType).getBankAccessNo();
    }

    public String getChinaBankMch(String businessOrderType) {
       return chinaBankMap.get(businessOrderType).getBankMch();
    }

    public String getFuminBankMch(String businessOrderType) {
        return fuminBankMap.get(businessOrderType).getBankMch();
    }

    public boolean isFuminBankAggregatePay(String businessOrderType) {
        return isFuminBankAggregatePay(businessOrderType, StringUtils.EMPTY, StringUtils.EMPTY);
    }

    public boolean isFuminBankAggregatePay(String businessOrderType, String receiverId, String payMode) {
        if(StringUtils.isBlank(businessOrderType)) {
            return false;
        }

        if(businessOrderType.contains(BusinessTypeEnum.EC.getType())) {
            return fuminBankPayConfigMap.containsKey(businessOrderType);
        }
        else if(businessOrderType.contains(BusinessTypeEnum.YIKUAIQIAN.getType())) {
            if(!fuminBankPayConfigMap.containsKey(businessOrderType)) {
                return false;
            }

            String supportPayModes = ApolloUtil.getProperty(Constants.FBANK_HEYE_SUPPORT_PAY_MODE, StringUtils.EMPTY);

            // 1.不支持的支付方式走直连
            if(!supportPayModes.contains(payMode)) {
                return false;
            }

            String graySwitch = ApolloUtil.getProperty(Constants.FBANK_HEYE_GRAY_SWITCH, StringUtils.EMPTY);

            // 2.灰度开关关闭走富民
            if(!Boolean.parseBoolean(graySwitch)) {
                return true;
            }

            // 3.在灰度列表走直连
            return ApolloUtil.getProperty(Constants.FBANK_HEYE_GRAY_SHOPS, StringUtils.EMPTY).contains(receiverId);
        }

        return false;
    }

    public String checkRoute(String businessType, String accountId) {
        /**
         * NOTICE:由于业务从中行->富民，富民优先级高于中行
         */
        if(fuminBankMap.containsKey(businessType)) {
            try {
                switch(getChannelType(accountId)) {
                    case Constants.YOP:
                        return Constants.CHANNEL_YOP_NAME;
                    case Constants.PINGAN:
                        return Constants.CHANNEL_PINGAN_NAME;
                    default:
                        return Constants.CHANNEL_FUMIN_BANK_NAME;
                }
            }
            catch(Exception ex) {
                log.error(ex.getMessage(), ex);
                return Constants.CHANNEL_FUMIN_BANK_NAME;
            }
        }

        if(chinaBankMap.containsKey(businessType)) {
            return Constants.CHANNEL_CHINA_BANK_NAME;
        }

        return null;
    }

    private void castVal2ChinaBankConfig(String val, Map<String, ChinaBankBusinessConfig> tmpChinaBankMap) {
        List<ChinaBankBusinessConfig> list = JSONUtils.parse2List(val, ChinaBankBusinessConfig.class);

        for(ChinaBankBusinessConfig c : list) {
            tmpChinaBankMap.put(c.getBusinessOrderType(), c);
        }
    }

    private void castVal2FuminBankConfig (String val, Map<String, FuminBankBusinessConfig> tmpChinaBankMap) {
        List<FuminBankBusinessConfig> list = JSONUtils.parse2List(val, FuminBankBusinessConfig.class);

        for(FuminBankBusinessConfig c : list) {
            tmpChinaBankMap.put(c.getBusinessOrderType(), c);
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        //初始话配置
        initConfig();
        initFuminPayConfig();
        initProfitSharingAccountConfig();
        Set set = new HashSet();
        set.add(ApolloConstants.BANK_BUSINESS_ORDER_CONFIG_NEW);
        set.add(ApolloConstants.BANK_FUMIN_PAY_CONFIG);
        set.add(ApolloConstants.PROFIT_SHARING_ACCOUNT_CONFIG);
        //动态监听配置变化
        Config config = ConfigService.getAppConfig();
        config.addChangeListener(new ConfigChangeListener() {
            @Override
            public void onChange(ConfigChangeEvent changeEvent) {
                initConfig();
                initFuminPayConfig();
                initProfitSharingAccountConfig();
                log.info("ChinaBankAdapter#onChange change configs success chinaBankMap:{} fuminBankMap:{}, fuminBankPayConfigMap：{}, profitSharingAccountConfig：{}"
                        ,JSONUtils.toJSON(chinaBankMap), JSONUtils.toJSON(fuminBankMap), JSONUtils.toJSON(fuminBankPayConfigMap), JSONUtils.toJSON(profitSharingAccountConfig));
            }
        }, set);
    }

    /**
     * 获取渠道
     */
    private String getChannelType(String accountId) {
        if(StringUtils.isBlank(accountId)) {
            return StringUtils.EMPTY;
        }

        ThirdUserInfo thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(accountId);

        if(thirdUserInfo == null) {
            return StringUtils.EMPTY;
        }

        return thirdUserInfo.getThirdType();
    }
}
